if (!dojo._hasResource["dojo._base.html"]) { //_hasResource checks added by build. Do not use _hasResource directly in your code.
    dojo._hasResource["dojo._base.html"] = true;
    dojo.require("dojo._base.lang");
    dojo.provide("dojo._base.html");

    // FIXME: need to add unit tests for all the semi-public methods

    try {
        document.execCommand("BackgroundImageCache", false, true);
    } catch(e) {
        // sane browsers don't have cache "issues"
    }

    // =============================
    // DOM Functions
    // =============================

    /*=====
     dojo.byId = function(id, doc){
     //	summary:
     //		Returns DOM node with matching `id` attribute or `null`
     //		if not found, similar to "$" function in another library.
     //		If `id` is a DomNode, this function is a no-op.
     //
     //	id: String|DOMNode
     //	 	A string to match an HTML id attribute or a reference to a DOM Node
     //
     //	doc: Document?
     //		Document to work in. Defaults to the current value of
     //		dojo.doc.  Can be used to retrieve
     //		node references from other documents.
     =====*/
    if (dojo.isIE || dojo.isOpera) {
        dojo.byId = function(id, doc) {
            if (dojo.isString(id)) {
                var _d = doc || dojo.doc;
                var te = _d.getElementById(id);
                // attributes.id.value is better than just id in case the
                // user has a name=id inside a form
                if (te && te.attributes.id.value == id) {
                    return te;
                } else {
                    var eles = _d.all[id];
                    if (!eles || !eles.length) {
                        return eles;
                    }
                    // if more than 1, choose first with the correct id
                    var i = 0;
                    while ((te = eles[i++])) {
                        if (te.attributes.id.value == id) {
                            return te;
                        }
                    }
                }
            } else {
                return id; // DomNode
            }
        }
    } else {
        dojo.byId = function(id, doc) {
            return dojo.isString(id) ? (doc || dojo.doc).getElementById(id) : id; // DomNode
        }
    }
    /*=====
     }
     =====*/

    (function() {
        /*
         dojo.createElement = function(obj, parent, position){
         // TODO: need to finish this!
         }
         */

        var d = dojo;

        var _destroyContainer = null;
        dojo.addOnUnload(function() {
            _destroyContainer = null; //prevent IE leak
        });
        dojo._destroyElement = function(/*String||DomNode*/node) {
            // summary:
            //		removes node from its parent, clobbers it and all of its
            //		children.
            //	node:
            //		the element to be destroyed, either as an ID or a reference

            node = d.byId(node);
            try {
                if (!_destroyContainer) {
                    _destroyContainer = document.createElement("div");
                }
                _destroyContainer.appendChild(node.parentNode ? node.parentNode.removeChild(node) :
                                              node);
                // NOTE: see http://trac.dojotoolkit.org/ticket/2931. This may be a bug and not a feature
                _destroyContainer.innerHTML = "";
            } catch(e) {
                /* squelch */
            }
        };

        dojo.isDescendant = function(/*DomNode|String*/node, /*DomNode|String*/ancestor) {
            //	summary:
            //		Returns true if node is a descendant of ancestor
            //	node: id or node reference to test
            //	ancestor: id or node reference of potential parent to test against
            try {
                node = d.byId(node);
                ancestor = d.byId(ancestor);
                while (node) {
                    if (node === ancestor) {
                        return true; // Boolean
                    }
                    node = node.parentNode;
                }
            } catch(e) { /* squelch, return false */
            }
            return false; // Boolean
        };

        dojo.setSelectable = function(/*DomNode|String*/node, /*Boolean*/selectable) {
            //	summary: enable or disable selection on a node
            //	node:
            //		id or reference to node
            //	selectable:
            node = d.byId(node);
            if (d.isMozilla) {
                node.style.MozUserSelect = selectable ? "" : "none";
            } else if (d.isKhtml) {
                node.style.KhtmlUserSelect = selectable ? "auto" : "none";
            } else if (d.isIE) {
                node.unselectable = selectable ? "" : "on";
                d.query("*", node).forEach(function(descendant) {
                    descendant.unselectable = selectable ? "" : "on";
                });
            }
            //FIXME: else?  Opera?
        };

        var _insertBefore = function(/*Node*/node, /*Node*/ref) {
            ref.parentNode.insertBefore(node, ref);
            return true;	//	boolean
        }

        var _insertAfter = function(/*Node*/node, /*Node*/ref) {
            //	summary:
            //		Try to insert node after ref
            var pn = ref.parentNode;
            if (ref == pn.lastChild) {
                pn.appendChild(node);
            } else {
                return _insertBefore(node, ref.nextSibling);	//	boolean
            }
            return true;	//	boolean
        }

        dojo.place =
        function(/*String|DomNode*/node, /*String|DomNode*/refNode, /*String|Number*/position) {
            //	summary:
            //		Attempt to insert node into the DOM, choosing from various positioning options.
            //		Returns true if successful, false otherwise.
            //	node:
            //		id or node reference to place relative to refNode
            //	refNode:
            //		id or node reference to use as basis for placement
            //	position:
            //		string noting the position of node relative to refNode or a
            //		number indicating the location in the childNodes collection of
            //		refNode. Accepted string values are:
            //
            //		* before
            //		* after
            //		* first
            //		* last
            //
            //		"first" and "last" indicate positions as children of refNode.

            // FIXME: need to write tests for this!!!!
            if (!node || !refNode || position === undefined) {
                return false;	//	boolean
            }
            node = d.byId(node);
            refNode = d.byId(refNode);
            if (typeof position == "number") {
                var cn = refNode.childNodes;
                if ((position == 0 && cn.length == 0) ||
                    cn.length == position) {
                    refNode.appendChild(node);
                    return true;
                }
                if (position == 0) {
                    return _insertBefore(node, refNode.firstChild);
                }
                return _insertAfter(node, cn[position - 1]);
            }
            switch (position.toLowerCase()) {
                case "before":
                    return _insertBefore(node, refNode);    //	boolean
                case "after":
                    return _insertAfter(node, refNode);        //	boolean
                case "first":
                    if (refNode.firstChild) {
                        return _insertBefore(node, refNode.firstChild);	//	boolean
                    }
                // else fallthrough...
                default: // aka: last
                    refNode.appendChild(node);
                    return true;    //	boolean
            }
        }

        // Box functions will assume this model.
        // On IE/Opera, BORDER_BOX will be set if the primary document is in quirks mode.
        // Can be set to change behavior of box setters.

        // can be either:
        //	"border-box"
        //	"content-box" (default)
        dojo.boxModel = "content-box";

        // We punt per-node box mode testing completely.
        // If anybody cares, we can provide an additional (optional) unit
        // that overrides existing code to include per-node box sensitivity.

        // Opera documentation claims that Opera 9 uses border-box in BackCompat mode.
        // but experiments (Opera 9.10.8679 on Windows Vista) indicate that it actually continues to use content-box.
        // IIRC, earlier versions of Opera did in fact use border-box.
        // Opera guys, this is really confusing. Opera being broken in quirks mode is not our fault.

        if (d.isIE /*|| dojo.isOpera*/) {
            var _dcm = document.compatMode;
            // client code may have to adjust if compatMode varies across iframes
            d.boxModel = _dcm == "BackCompat" || _dcm == "QuirksMode" || d.isIE < 6 ? "border-box" :
                         "content-box"; // FIXME: remove IE < 6 support?
        }

        // =============================
        // Style Functions
        // =============================

        // getComputedStyle drives most of the style code.
        // Wherever possible, reuse the returned object.
        //
        // API functions below that need to access computed styles accept an
        // optional computedStyle parameter.
        // If this parameter is omitted, the functions will call getComputedStyle themselves.
        // This way, calling code can access computedStyle once, and then pass the reference to
        // multiple API functions.

        /*=====
         dojo.getComputedStyle = function(node){
         //	summary:
         //		Returns a "computed style" object.
         //
         //	description:
         //		Gets a "computed style" object which can be used to gather
         //		information about the current state of the rendered node.
         //
         //		Note that this may behave differently on different browsers.
         //		Values may have different formats and value encodings across
         //		browsers.
         //
         //		Note also that this method is expensive.  Wherever possible,
         //		reuse the returned object.
         //
         //		Use the dojo.style() method for more consistent (pixelized)
         //		return values.
         //
         //	node: DOMNode
         //		A reference to a DOM node. Does NOT support taking an
         //		ID string for speed reasons.
         //	example:
         //	|	dojo.getComputedStyle(dojo.byId('foo')).borderWidth;
         return; // CSS2Properties
         }
         =====*/

        var gcs, dv = document.defaultView;
        if (d.isSafari) {
            gcs = function(/*DomNode*/node) {
                var s = dv.getComputedStyle(node, null);
                if (!s && node.style) {
                    node.style.display = "";
                    s = dv.getComputedStyle(node, null);
                }
                return s || {};
            };
        } else if (d.isIE) {
            gcs = function(node) {
                return node.currentStyle;
            };
        } else {
            gcs = function(node) {
                return dv.getComputedStyle(node, null);
            };
        }
        dojo.getComputedStyle = gcs;

        if (!d.isIE) {
            dojo._toPixelValue = function(element, value) {
                // style values can be floats, client code may want
                // to round for integer pixels.
                return parseFloat(value) || 0;
            }
        } else {
            dojo._toPixelValue = function(element, avalue) {
                if (!avalue) {
                    return 0;
                }
                // on IE7, medium is usually 4 pixels
                if (avalue == "medium") {
                    return 4;
                }
                // style values can be floats, client code may
                // want to round this value for integer pixels.
                if (avalue.slice && (avalue.slice(-2) == 'px')) {
                    return parseFloat(avalue);
                }
                with (element) {
                    var sLeft = style.left;
                    var rsLeft = runtimeStyle.left;
                    runtimeStyle.left = currentStyle.left;
                    try {
                        // 'avalue' may be incompatible with style.left, which can cause IE to throw
                        // this has been observed for border widths using "thin", "medium", "thick" constants
                        // those particular constants could be trapped by a lookup
                        // but perhaps there are more
                        style.left = avalue;
                        avalue = style.pixelLeft;
                    } catch(e) {
                        avalue = 0;
                    }
                    style.left = sLeft;
                    runtimeStyle.left = rsLeft;
                }
                return avalue;
            }
        }
        var px = d._toPixelValue;

        // FIXME: there opacity quirks on FF that we haven't ported over. Hrm.
        /*=====
         dojo._getOpacity = function(node){
         //	summary:
         //		Returns the current opacity of the passed node as a
         //		floating-point value between 0 and 1.
         //	node: DomNode
         //		a reference to a DOM node. Does NOT support taking an
         //		ID string for speed reasons.
         //	return: Number between 0 and 1
         }
         =====*/

        dojo._getOpacity = d.isIE ? function(node) {
            try {
                return node.filters.alpha.opacity / 100; // Number
            } catch(e) {
                return 1; // Number
            }
        } : function(node) {
            return gcs(node).opacity;
        };

        /*=====
         dojo._setOpacity = function(node, opacity){
         //	summary:
         //		set the opacity of the passed node portably. Returns the
         //		new opacity of the node.
         //	node: DOMNode
         //		a reference to a DOM node. Does NOT support taking an
         //		ID string for performance reasons.
         //	opacity: Number
         //		A Number between 0 and 1. 0 specifies transparent.
         //	return: Number between 0 and 1
         }
         =====*/

        dojo._setOpacity = d.isIE ? function(/*DomNode*/node, /*Number*/opacity) {
            if (opacity == 1) {
                // on IE7 Alpha(Filter opacity=100) makes text look fuzzy so remove it altogether (bug #2661)
                var filterRE = /FILTER:[^;]*;?/i;
                node.style.cssText = node.style.cssText.replace(filterRE, "");
                if (node.nodeName.toLowerCase() == "tr") {
                    d.query("> td", node).forEach(function(i) {
                        i.style.cssText = i.style.cssText.replace(filterRE, "");
                    });
                }
            } else {
                var o = "Alpha(Opacity=" + opacity * 100 + ")";
                node.style.filter = o;
            }
            if (node.nodeName.toLowerCase() == "tr") {
                d.query("> td", node).forEach(function(i) {
                    i.style.filter = o;
                });
            }
            return opacity;
        } : function(node, opacity) {
            return node.style.opacity = opacity;
        };

        var _pixelNamesCache = {
            left: true, top: true
        };
        var _pixelRegExp = /margin|padding|width|height|max|min|offset/;  // |border
        var _toStyleValue = function(node, type, value) {
            type = type.toLowerCase();
            if (d.isIE && value == "auto") {
                if (type == "height") {
                    return node.offsetHeight;
                }
                if (type == "width") {
                    return node.offsetWidth;
                }
            }
            if (!(type in _pixelNamesCache)) {
                //	if(dojo.isOpera && type == "cssText"){
                // 		FIXME: add workaround for #2855 here
                //	}
                _pixelNamesCache[type] = _pixelRegExp.test(type);
            }
            return _pixelNamesCache[type] ? px(node, value) : value;
        }

        var _floatStyle = d.isIE ? "styleFloat" : "cssFloat";
        var _floatAliases = { "cssFloat": _floatStyle, "styleFloat": _floatStyle, "float": _floatStyle };

        // public API

        dojo.style = function(/*DomNode|String*/ node,
            /*String?|Object?*/ style,
            /*String?*/ value) {
            //	summary:
            //		Accesses styles on a node. If 2 arguments are
            //		passed, acts as a getter. If 3 arguments are passed, acts
            //		as a setter.
            //	node:
            //		id or reference to node to get/set style for
            //	style:
            //		the style property to set in DOM-accessor format
            //		("borderWidth", not "border-width") or an object with key/value
            //		pairs suitable for setting each property.
            //	value:
            //		If passed, sets value on the node for style, handling
            //		cross-browser concerns.
            //	example:
            //		Passing only an ID or node returns the computed style object of
            //		the node:
            //	|	dojo.style("thinger");
            //	example:
            //		Passing a node and a style property returns the current
            //		normalized, computed value for that property:
            //	|	dojo.style("thinger", "opacity"); // 1 by default
            //
            //	example:
            //		Passing a node, a style property, and a value changes the
            //		current display of the node and returns the new computed value
            //	|	dojo.style("thinger", "opacity", 0.5); // == 0.5
            //
            //	example:
            //		Passing a node, an object-style style property sets each of the values in turn and returns the computed style object of the node:
            //	|	dojo.style("thinger", {
            //	|		"opacity": 0.5,
            //	|		"border": "3px solid black",
            //	|		"height": 300
            //	|	});
            //
            // 	example:
            //		When the CSS style property is hyphenated, the JavaScript property is camelCased.
            //		font-size becomes fontSize, and so on.
            //	|	dojo.style("thinger",{
            //	|		fontSize:"14pt",
            //	|		letterSpacing:"1.2em"
            //	|	});
            //
            //	example:
            //		dojo.NodeList implements .style() using the same syntax, omitting the "node" parameter, calling
            //		dojo.style() on every element of the list. See: dojo.query and dojo.NodeList
            //	|	dojo.query(".someClassName").style("visibility","hidden");
            //	|	// or
            //	|	dojo.query("#baz > div").style({
            //	|		opacity:0.75,
            //	|		fontSize:"13pt"
            //	|	});

            var n = d.byId(node), args = arguments.length, op = (style == "opacity");
            style = _floatAliases[style] || style;
            if (args == 3) {
                return op ? d._setOpacity(n, value) : n.style[style] = value;
                /*Number*/
            }
            if (args == 2 && op) {
                return d._getOpacity(n);
            }
            var s = gcs(n);
            if (args == 2 && !d.isString(style)) {
                for (var x in style) {
                    d.style(node, x, style[x]);
                }
                return s;
            }
            return (args == 1) ? s : _toStyleValue(n, style, s[style]);
            /* CSS2Properties||String||Number */
        }

        // =============================
        // Box Functions
        // =============================

        dojo._getPadExtents = function(/*DomNode*/n, /*Object*/computedStyle) {
            //	summary:
            // 		Returns object with special values specifically useful for node
            // 		fitting.
            //
            // 		* l/t = left/top padding (respectively)
            // 		* w = the total of the left and right padding
            // 		* h = the total of the top and bottom padding
            //
            //		If 'node' has position, l/t forms the origin for child nodes.
            //		The w/h are used for calculating boxes.
            //		Normally application code will not need to invoke this
            //		directly, and will use the ...box... functions instead.
            var
                    s = computedStyle || gcs(n),
                    l = px(n, s.paddingLeft),
                    t = px(n, s.paddingTop);
            return {
                l: l,
                t: t,
                w: l + px(n, s.paddingRight),
                h: t + px(n, s.paddingBottom)
            };
        }

        dojo._getBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle) {
            //	summary:
            //		returns an object with properties useful for noting the border
            //		dimensions.
            //
            // 		* l/t = the sum of left/top border (respectively)
            //		* w = the sum of the left and right border
            //		* h = the sum of the top and bottom border
            //
            //		The w/h are used for calculating boxes.
            //		Normally application code will not need to invoke this
            //		directly, and will use the ...box... functions instead.
            var
                    ne = "none",
                    s = computedStyle || gcs(n),
                    bl = (s.borderLeftStyle != ne ? px(n, s.borderLeftWidth) : 0),
                    bt = (s.borderTopStyle != ne ? px(n, s.borderTopWidth) : 0);
            return {
                l: bl,
                t: bt,
                w: bl + (s.borderRightStyle != ne ? px(n, s.borderRightWidth) : 0),
                h: bt + (s.borderBottomStyle != ne ? px(n, s.borderBottomWidth) : 0)
            };
        }

        dojo._getPadBorderExtents = function(/*DomNode*/n, /*Object*/computedStyle) {
            //	summary:
            //		returns object with properties useful for box fitting with
            //		regards to padding.
            //
            //		* l/t = the sum of left/top padding and left/top border (respectively)
            //		* w = the sum of the left and right padding and border
            //		* h = the sum of the top and bottom padding and border
            //
            //		The w/h are used for calculating boxes.
            //		Normally application code will not need to invoke this
            //		directly, and will use the ...box... functions instead.
            var
                    s = computedStyle || gcs(n),
                    p = d._getPadExtents(n, s),
                    b = d._getBorderExtents(n, s);
            return {
                l: p.l + b.l,
                t: p.t + b.t,
                w: p.w + b.w,
                h: p.h + b.h
            };
        }

        dojo._getMarginExtents = function(n, computedStyle) {
            //	summary:
            //		returns object with properties useful for box fitting with
            //		regards to box margins (i.e., the outer-box).
            //
            //		* l/t = marginLeft, marginTop, respectively
            //		* w = total width, margin inclusive
            //		* h = total height, margin inclusive
            //
            //		The w/h are used for calculating boxes.
            //		Normally application code will not need to invoke this
            //		directly, and will use the ...box... functions instead.
            var
                    s = computedStyle || gcs(n),
                    l = px(n, s.marginLeft),
                    t = px(n, s.marginTop),
                    r = px(n, s.marginRight),
                    b = px(n, s.marginBottom);
            if (d.isSafari && (s.position != "absolute")) {
                // FIXME: Safari's version of the computed right margin
                // is the space between our right edge and the right edge
                // of our offsetParent.
                // What we are looking for is the actual margin value as
                // determined by CSS.
                // Hack solution is to assume left/right margins are the same.
                r = l;
            }
            return {
                l: l,
                t: t,
                w: l + r,
                h: t + b
            };
        }

        // Box getters work in any box context because offsetWidth/clientWidth
        // are invariant wrt box context
        //
        // They do *not* work for display: inline objects that have padding styles
        // because the user agent ignores padding (it's bogus styling in any case)
        //
        // Be careful with IMGs because they are inline or block depending on
        // browser and browser mode.

        // Although it would be easier to read, there are not separate versions of
        // _getMarginBox for each browser because:
        // 1. the branching is not expensive
        // 2. factoring the shared code wastes cycles (function call overhead)
        // 3. duplicating the shared code wastes bytes

        dojo._getMarginBox = function(/*DomNode*/node, /*Object*/computedStyle) {
            // summary:
            //		returns an object that encodes the width, height, left and top
            //		positions of the node's margin box.
            var s = computedStyle || gcs(node), me = d._getMarginExtents(node, s);
            var l = node.offsetLeft - me.l,    t = node.offsetTop - me.t;
            if (d.isMoz) {
                // Mozilla:
                // If offsetParent has a computed overflow != visible, the offsetLeft is decreased
                // by the parent's border.
                // We don't want to compute the parent's style, so instead we examine node's
                // computed left/top which is more stable.
                var sl = parseFloat(s.left), st = parseFloat(s.top);
                if (!isNaN(sl) && !isNaN(st)) {
                    l = sl,t = st;
                } else {
                    // If child's computed left/top are not parseable as a number (e.g. "auto"), we
                    // have no choice but to examine the parent's computed style.
                    var p = node.parentNode;
                    if (p && p.style) {
                        var pcs = gcs(p);
                        if (pcs.overflow != "visible") {
                            var be = d._getBorderExtents(p, pcs);
                            l += be.l,t += be.t;
                        }
                    }
                }
            } else if (d.isOpera) {
                // On Opera, offsetLeft includes the parent's border
                var p = node.parentNode;
                if (p) {
                    var be = d._getBorderExtents(p);
                    l -= be.l,t -= be.t;
                }
            }
            return {
                l: l,
                t: t,
                w: node.offsetWidth + me.w,
                h: node.offsetHeight + me.h
            };
        }

        dojo._getContentBox = function(node, computedStyle) {
            // summary:
            //		Returns an object that encodes the width, height, left and top
            //		positions of the node's content box, irrespective of the
            //		current box model.

            // clientWidth/Height are important since the automatically account for scrollbars
            // fallback to offsetWidth/Height for special cases (see #3378)
            var s = computedStyle ||
                    gcs(node), pe = d._getPadExtents(node, s), be = d._getBorderExtents(node, s), w = node.clientWidth, h;
            if (!w) {
                w = node.offsetWidth,h = node.offsetHeight;
            } else {
                h = node.clientHeight,be.w = be.h = 0;
            }
            // On Opera, offsetLeft includes the parent's border
            if (d.isOpera) {
                pe.l += be.l;
                pe.t += be.t;
            }
            ;
            return {
                l: pe.l,
                t: pe.t,
                w: w - pe.w - be.w,
                h: h - pe.h - be.h
            };
        }

        dojo._getBorderBox = function(node, computedStyle) {
            var s = computedStyle ||
                    gcs(node), pe = d._getPadExtents(node, s), cb = d._getContentBox(node, s);
            return {
                l: cb.l - pe.l,
                t: cb.t - pe.t,
                w: cb.w + pe.w,
                h: cb.h + pe.h
            };
        }

        // Box setters depend on box context because interpretation of width/height styles
        // vary wrt box context.
        //
        // The value of dojo.boxModel is used to determine box context.
        // dojo.boxModel can be set directly to change behavior.
        //
        // Beware of display: inline objects that have padding styles
        // because the user agent ignores padding (it's a bogus setup anyway)
        //
        // Be careful with IMGs because they are inline or block depending on
        // browser and browser mode.
        //
        // Elements other than DIV may have special quirks, like built-in
        // margins or padding, or values not detectable via computedStyle.
        // In particular, margins on TABLE do not seems to appear
        // at all in computedStyle on Mozilla.

        dojo._setBox =
        function(/*DomNode*/node, /*Number?*/l, /*Number?*/t, /*Number?*/w, /*Number?*/
                            h, /*String?*/u) {
            //	summary:
            //		sets width/height/left/top in the current (native) box-model
            //		dimentions. Uses the unit passed in u.
            //	node: DOM Node reference. Id string not supported for performance reasons.
            //	l: optional. left offset from parent.
            //	t: optional. top offset from parent.
            //	w: optional. width in current box model.
            //	h: optional. width in current box model.
            //	u: optional. unit measure to use for other measures. Defaults to "px".
            u = u || "px";
            var s = node.style;
            if (!isNaN(l)) {
                s.left = l + u;
            }
            if (!isNaN(t)) {
                s.top = t + u;
            }
            if (w >= 0) {
                s.width = w + u;
            }
            if (h >= 0) {
                s.height = h + u;
            }
        }

        dojo._usesBorderBox = function(/*DomNode*/node) {
            //	summary:
            //		True if the node uses border-box layout.

            // We could test the computed style of node to see if a particular box
            // has been specified, but there are details and we choose not to bother.
            var n = node.tagName;
            // For whatever reason, TABLE and BUTTON are always border-box by default.
            // If you have assigned a different box to either one via CSS then
            // box functions will break.
            return d.boxModel == "border-box" || n == "TABLE" || n == "BUTTON"; // boolean
        }

        dojo._setContentSize =
        function(/*DomNode*/node, /*Number*/widthPx, /*Number*/heightPx, /*Object*/computedStyle) {
            //	summary:
            //		Sets the size of the node's contents, irrespective of margins,
            //		padding, or borders.
            if (d._usesBorderBox(node)) {
                var pb = d._getPadBorderExtents(node, computedStyle);
                if (widthPx >= 0) {
                    widthPx += pb.w;
                }
                if (heightPx >= 0) {
                    heightPx += pb.h;
                }
            }
            d._setBox(node, NaN, NaN, widthPx, heightPx);
        }

        dojo._setMarginBox = function(/*DomNode*/node, /*Number?*/leftPx, /*Number?*/topPx,
            /*Number?*/widthPx, /*Number?*/heightPx,
            /*Object*/computedStyle) {
            //	summary:
            //		sets the size of the node's margin box and placement
            //		(left/top), irrespective of box model. Think of it as a
            //		passthrough to dojo._setBox that handles box-model vagaries for
            //		you.

            var s = computedStyle || gcs(node);
            // Some elements have special padding, margin, and box-model settings.
            // To use box functions you may need to set padding, margin explicitly.
            // Controlling box-model is harder, in a pinch you might set dojo.boxModel.
            var bb = d._usesBorderBox(node),
                    pb = bb ? _nilExtents : d._getPadBorderExtents(node, s),
                    mb = d._getMarginExtents(node, s);
            if (widthPx >= 0) {
                widthPx = Math.max(widthPx - pb.w - mb.w, 0);
            }
            if (heightPx >= 0) {
                heightPx = Math.max(heightPx - pb.h - mb.h, 0);
            }
            d._setBox(node, leftPx, topPx, widthPx, heightPx);
        }

        var _nilExtents = { l:0, t:0, w:0, h:0 };

        // public API

        dojo.marginBox = function(/*DomNode|String*/node, /*Object?*/box) {
            //	summary:
            //		Getter/setter for the margin-box of node.
            //	description:
            //		Returns an object in the expected format of box (regardless
            //		if box is passed). The object might look like:
            //			`{ l: 50, t: 200, w: 300: h: 150 }`
            //		for a node offset from its parent 50px to the left, 200px from
            //		the top with a margin width of 300px and a margin-height of
            //		150px.
            //	node:
            //		id or reference to DOM Node to get/set box for
            //	box:
            //		If passed, denotes that dojo.marginBox() should
            //		update/set the margin box for node. Box is an object in the
            //		above format. All properties are optional if passed.
            var n = d.byId(node), s = gcs(n), b = box;
            return !b ? d._getMarginBox(n, s) : d._setMarginBox(n, b.l, b.t, b.w, b.h, s); // Object
        }

        dojo.contentBox = function(/*DomNode|String*/node, /*Object?*/box) {
            //	summary:
            //		Getter/setter for the content-box of node.
            //	description:
            //		Returns an object in the expected format of box (regardless if box is passed).
            //		The object might look like:
            //			`{ l: 50, t: 200, w: 300: h: 150 }`
            //		for a node offset from its parent 50px to the left, 200px from
            //		the top with a content width of 300px and a content-height of
            //		150px. Note that the content box may have a much larger border
            //		or margin box, depending on the box model currently in use and
            //		CSS values set/inherited for node.
            //	node:
            //		id or reference to DOM Node to get/set box for
            //	box:
            //		If passed, denotes that dojo.contentBox() should
            //		update/set the content box for node. Box is an object in the
            //		above format. All properties are optional if passed.
            var n = dojo.byId(node), s = gcs(n), b = box;
            return !b ? d._getContentBox(n, s) : d._setContentSize(n, b.w, b.h, s); // Object
        }

        // =============================
        // Positioning
        // =============================

        var _sumAncestorProperties = function(node, prop) {
            if (!(node = (node || 0).parentNode)) {
                return 0
            }
            ;
            var val, retVal = 0, _b = d.body();
            while (node && node.style) {
                if (gcs(node).position == "fixed") {
                    return 0;
                }
                val = node[prop];
                if (val) {
                    retVal += val - 0;
                    // opera and khtml #body & #html has the same values, we only
                    // need one value
                    if (node == _b) {
                        break;
                    }
                }
                node = node.parentNode;
            }
            return retVal;	//	integer
        }

        dojo._docScroll = function() {
            var
                    _b = d.body(),
                    _w = d.global,
                    de = d.doc.documentElement;
            return {
                y: (_w.pageYOffset || de.scrollTop || _b.scrollTop || 0),
                x: (_w.pageXOffset || d._fixIeBiDiScrollLeft(de.scrollLeft) || _b.scrollLeft || 0)
            };
        };

        dojo._isBodyLtr = function() {
            //FIXME: could check html and body tags directly instead of computed style?  need to ignore case, accept empty values
            return !("_bodyLtr" in d) ?
                   d._bodyLtr = gcs(d.body()).direction == "ltr" :
                   d._bodyLtr; // Boolean
        }

        dojo._getIeDocumentElementOffset = function() {
            // summary
            // The following values in IE contain an offset:
            //     event.clientX
            //     event.clientY
            //     node.getBoundingClientRect().left
            //     node.getBoundingClientRect().top
            // But other position related values do not contain this offset, such as
            // node.offsetLeft, node.offsetTop, node.style.left and node.style.top.
            // The offset is always (2, 2) in LTR direction. When the body is in RTL
            // direction, the offset counts the width of left scroll bar's width.
            // This function computes the actual offset.

            //NOTE: assumes we're being called in an IE browser

            var de = d.doc.documentElement;
            //FIXME: use this instead?			var de = d.compatMode == "BackCompat" ? d.body : d.documentElement;

            return (d.isIE >= 7) ?
                   {x: de.getBoundingClientRect().left, y: de.getBoundingClientRect().top}
                    :
                // IE 6.0
                   {x: d._isBodyLtr() || window.parent == window ?
                       de.clientLeft : de.offsetWidth - de.clientWidth - de.clientLeft,
                       y: de.clientTop}; // Object
        };

        dojo._fixIeBiDiScrollLeft = function(/*Integer*/ scrollLeft) {
            // In RTL direction, scrollLeft should be a negative value, but IE
            // returns a positive one. All codes using documentElement.scrollLeft
            // must call this function to fix this error, otherwise the position
            // will offset to right when there is a horizontal scrollbar.
            var dd = d.doc;
            if (d.isIE && !dojo._isBodyLtr()) {
                var de = dd.compatMode == "BackCompat" ? dd.body : dd.documentElement;
                return scrollLeft + de.clientWidth - de.scrollWidth; // Integer
            }
            return scrollLeft; // Integer
        }

        dojo._abs = function(/*DomNode*/node, /*Boolean?*/includeScroll) {
            //	summary:
            //		Gets the position of the passed element relative to
            //		the viewport (if includeScroll==false), or relative to the
            //		document root (if includeScroll==true).
            //
            //		Returns an object of the form:
            //			{ x: 100, y: 300 }
            //		if includeScroll is passed, the x and y values will include any
            //		document offsets that may affect the position relative to the
            //		viewport.

            // FIXME: need to decide in the brave-new-world if we're going to be
            // margin-box or border-box.
            var ownerDocument = node.ownerDocument;
            var ret = {
                x: 0,
                y: 0
            };

            // targetBoxType == "border-box"
            var db = d.body();
            if (d.isIE || (d.isFF >= 3)) {
                var client = node.getBoundingClientRect();
                var offset = (d.isIE) ? d._getIeDocumentElementOffset() : { x: 0, y: 0};
                ret.x = client.left - offset.x;
                ret.y = client.top - offset.y;
            } else if (ownerDocument["getBoxObjectFor"]) {
                // mozilla
                var bo = ownerDocument.getBoxObjectFor(node),
                        b = d._getBorderExtents(node);
                ret.x = bo.x - b.l - _sumAncestorProperties(node, "scrollLeft");
                ret.y = bo.y - b.t - _sumAncestorProperties(node, "scrollTop");
            } else {
                if (node["offsetParent"]) {
                    var endNode;
                    // in Safari, if the node is an absolutely positioned child of
                    // the body and the body has a margin the offset of the child
                    // and the body contain the body's margins, so we need to end
                    // at the body
                    // FIXME: getting contrary results to the above in latest WebKit.
                    if (d.isSafari &&
                        //(node.style.getPropertyValue("position") == "absolute") &&
                        (gcs(node).position == "absolute") &&
                        (node.parentNode == db)) {
                        endNode = db;
                    } else {
                        endNode = db.parentNode;
                    }
                    if (node.parentNode != db) {
                        var nd = node;
                        if (d.isOpera) {
                            nd = db;
                        }
                        ret.x -= _sumAncestorProperties(nd, "scrollLeft");
                        ret.y -= _sumAncestorProperties(nd, "scrollTop");
                    }
                    var curnode = node;
                    do{
                        var n = curnode.offsetLeft;
                        //FIXME: ugly hack to workaround the submenu in
                        //popupmenu2 does not shown up correctly in opera.
                        //Someone have a better workaround?
                        if (!d.isOpera || n > 0) {
                            ret.x += isNaN(n) ? 0 : n;
                        }
                        var t = curnode.offsetTop;
                        ret.y += isNaN(t) ? 0 : t;
                        if (d.isSafari && curnode != node) {
                            var cs = gcs(curnode);
                            ret.x += px(curnode, cs.borderLeftWidth);
                            ret.y += px(curnode, cs.borderTopWidth);
                        }
                        curnode = curnode.offsetParent;
                    } while ((curnode != endNode) && curnode);
                } else if (node.x && node.y) {
                    ret.x += isNaN(node.x) ? 0 : node.x;
                    ret.y += isNaN(node.y) ? 0 : node.y;
                }
            }
            // account for document scrolling
            // if offsetParent is used, ret value already includes scroll position
            // so we may have to actually remove that value if !includeScroll
            if (includeScroll) {
                var scroll = d._docScroll();
                ret.y += scroll.y;
                ret.x += scroll.x;
            }

            return ret; // object
        }

        // FIXME: need a setter for coords or a moveTo!!
        dojo.coords = function(/*DomNode|String*/node, /*Boolean?*/includeScroll) {
            //	summary:
            //		Returns an object that measures margin box width/height and
            //		absolute positioning data from dojo._abs().
            //
            //	description:
            //		Returns an object that measures margin box width/height and
            //		absolute positioning data from dojo._abs().
            //		Return value will be in the form:
            //			`{ l: 50, t: 200, w: 300: h: 150, x: 100, y: 300 }`
            //		Does not act as a setter. If includeScroll is passed, the x and
            //		y params are affected as one would expect in dojo._abs().
            var n = d.byId(node), s = gcs(n), mb = d._getMarginBox(n, s);
            var abs = d._abs(n, includeScroll);
            mb.x = abs.x;
            mb.y = abs.y;
            return mb;
        }

        // =============================
        // Element attribute Functions
        // =============================

        var _fixAttrName = function(/*String*/name) {
            switch (name.toLowerCase()) {
                case "tabindex":
                // Internet Explorer will only set or remove tabindex
                // if it is spelled "tabIndex"
                // console.debug((dojo.isIE && dojo.isIE < 8)? "tabIndex" : "tabindex");
                    return (d.isIE && d.isIE < 8) ? "tabIndex" : "tabindex";
                default:
                    return name;
            }
        }

        // non-deprecated HTML4 attributes with default values
        // http://www.w3.org/TR/html401/index/attributes.html
        // FF and Safari will return the default values if you
        // access the attributes via a property but not
        // via getAttribute()
        var _attrProps = {
            colspan: "colSpan",
            enctype: "enctype",
            frameborder: "frameborder",
            method: "method",
            rowspan: "rowSpan",
            scrolling: "scrolling",
            shape: "shape",
            span: "span",
            type: "type",
            valuetype: "valueType"
        }

        dojo.hasAttr = function(/*DomNode|String*/node, /*String*/name) {
            //	summary:
            //		Returns true if the requested attribute is specified on the
            //		given element, and false otherwise.
            //	node:
            //		id or reference to the element to check
            //	name:
            //		the name of the attribute
            //	returns:
            //		true if the requested attribute is specified on the
            //		given element, and false otherwise
            var attr = d.byId(node).getAttributeNode(_fixAttrName(name));
            return attr ? attr.specified : false; // Boolean
        }

        var _evtHdlrMap = {

        }

        var _ctr = 0;
        var _attrId = dojo._scopeName + "attrid";

        dojo.attr = function(/*DomNode|String*/node, /*String|Object*/name, /*String?*/value) {
            //	summary:
            //		Gets or sets an attribute on an HTML element.
            //	description:
            //		Handles normalized getting and setting of attributes on DOM
            //		Nodes. If 2 arguments are passed, and a the second argumnt is a
            //		string, acts as a getter.
            //
            //		If a third argument is passed, or if the second argumnt is a
            //		map of attributes, acts as a setter.
            //
            //		When passing functions as values, note that they will not be
            //		directly assigned to slots on the node, but rather the default
            //		behavior will be removed and the new behavior will be added
            //		using `dojo.connect()`, meaning that event handler properties
            //		will be normalized and that some caveats with regards to
            //		non-standard behaviors for onsubmit apply. Namely that you
            //		should cancel form submission using `dojo.stopEvent()` on the
            //		passed event object instead of returning a boolean value from
            //		the handler itself.
            //	node:
            //		id or reference to the element to get or set the attribute on
            //	name:
            //		the name of the attribute to get or set.
            //	value:
            //		The value to set for the attribute
            //	returns:
            //		when used as a getter, the value of the requested attribute
            //		or null if that attribute does not have a specified or
            //		default value;
            //
            //		when user as a setter, undefined
            //	example:
            //	|	// get the current value of the "foo" attribute on a node
            //	|	dojo.attr(dojo.byId("nodeId"), "foo");
            //	|
            //	|	// we can just pass the id:
            //	|	dojo.attr("nodeId", "foo");
            //	|
            //	|	// use attr() to set the tab index
            //	|	dojo.attr("nodeId", "tabindex", 3);
            //	|
            //	|	// set multiple values at once, including event handlers:
            //	|	dojo.attr("formId", {
            //	|		"foo": "bar",
            //	|		"tabindex": -1,
            //	|		"method": "POST",
            //	|		"onsubmit": function(e){
            //	|			// stop submitting the form. Note that the IE behavior
            //	|			// of returning true or false will have no effect here
            //	|			// since our handler is connect()ed to the built-in
            //	|			// onsubmit behavior and so we need to use
            //	|			// dojo.stopEvent() to ensure that the submission
            //	|			// doesn't proceed.
            //	|			dojo.stopEvent(e);
            //	|
            //	|			// submit the form with Ajax
            //	|			dojo.xhrPost({ form: "formId" });
            //	|		}
            //	|	});

            var args = arguments.length;
            if (args == 2 && !d.isString(name)) {
                for (var x in name) {
                    d.attr(node, x, name[x]);
                }
                return;
            }
            node = d.byId(node);
            name = _fixAttrName(name);
            if (args == 3) {
                if (d.isFunction(value)) {
                    // clobber if we can
                    var attrId = d.attr(node, _attrId);
                    if (!attrId) {
                        attrId = _ctr++;
                        d.attr(node, _attrId, attrId);
                    }
                    if (!_evtHdlrMap[attrId]) {
                        _evtHdlrMap[attrId] = {};
                    }
                    var h = _evtHdlrMap[attrId][name];
                    if (h) {
                        d.disconnect(h);
                    } else {
                        try {
                            delete node[name];
                        } catch(e) {
                        }
                    }

                    // ensure that event objects are normalized, etc.
                    _evtHdlrMap[attrId][name] = d.connect(node, name, value);

                } else if (typeof value == "boolean") { // e.g. onsubmit, disabled
                    // if a function, we should normalize the event object here!!!
                    node[name] = value;
                } else {
                    node.setAttribute(name, value);
                }
                return;
            } else {
                // should we access this attribute via a property or
                // via getAttribute()?
                var prop = _attrProps[name.toLowerCase()];
                if (prop) {
                    return node[prop];
                } else {
                    var value = node[name];
                    return (typeof value == 'boolean' || typeof value == 'function') ? value :
                           (d.hasAttr(node, name) ? node.getAttribute(name) : null);
                }
            }
        }

        dojo.removeAttr = function(/*DomNode|String*/node, /*String*/name) {
            //	summary:
            //		Removes an attribute from an HTML element.
            //	node:
            //		id or reference to the element to remove the attribute from
            //	name:
            //		the name of the attribute to remove
            d.byId(node).removeAttribute(_fixAttrName(name));
        }
    })();

    // =============================
    // (CSS) Class Functions
    // =============================

    dojo.hasClass = function(/*DomNode|String*/node, /*String*/classStr) {
        //	summary:
        //		Returns whether or not the specified classes are a portion of the
        //		class list currently applied to the node.
        return ((" " + dojo.byId(node).className + " ").indexOf(" " + classStr + " ") >= 0);  // Boolean
    };

    dojo.addClass = function(/*DomNode|String*/node, /*String*/classStr) {
        //	summary:
        //		Adds the specified classes to the end of the class list on the
        //		passed node.
        node = dojo.byId(node);
        var cls = node.className;
        if ((" " + cls + " ").indexOf(" " + classStr + " ") < 0) {
            node.className = cls + (cls ? ' ' : '') + classStr;
        }
    };

    dojo.removeClass = function(/*DomNode|String*/node, /*String*/classStr) {
        // summary: Removes the specified classes from node.
        node = dojo.byId(node);
        var t = dojo.trim((" " + node.className + " ").replace(" " + classStr + " ", " "));
        if (node.className != t) {
            node.className = t;
        }
    };

    dojo.toggleClass = function(/*DomNode|String*/node, /*String*/classStr, /*Boolean?*/condition) {
        //	summary:
        //		Adds a class to node if not present, or removes if present.
        //		Pass a boolean condition if you want to explicitly add or remove.
        //	condition:
        //		If passed, true means to add the class, false means to remove.
        if (condition === undefined) {
            condition = !dojo.hasClass(node, classStr);
        }
        dojo[condition ? "addClass" : "removeClass"](node, classStr);
    };

}
